from fastapi import APIRouter,  Request,  Query
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.templating import Jinja2Templates
from jose import jwt, JWTError
from cache import redis_cache  # 导入 Redis 缓存
from auth import models
from auth.utils import get_current_user_info, SECRET_KEY, ALGORITHM
from devices.models import Device
from .models import ECGRecord
from auth.models import Alert, User
from datetime import timezone
from typing import Optional
from datetime import datetime, timedelta
from sqlalchemy.orm import Session
from sqlalchemy import text
from fastapi import Form, Depends
from auth.db import SessionLocal
import numpy as np
from logging_config import setup_logging
from collections import OrderedDict
logger=setup_logging()
CST = timezone(timedelta(hours=8))
def now_cst():
    """返回当前北京时间（带时区信息）"""
    return datetime.now(CST)

router = APIRouter()
templates = Jinja2Templates(directory="templates")

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

def get_current_user(request: Request):
    token_cookie = request.cookies.get("access_token")
    if not token_cookie:
        return None
    try:
        token = token_cookie.split(" ")[1]
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return payload.get("sub")
    except JWTError:
        return None

# ---------------- 心电数据页 ----------------
@router.get("/ecg", response_class=HTMLResponse)
def ecg_page(request: Request, page: int = 1, q: str = "", db: Session = Depends(get_db)):  # 添加 q 参数
    username, role = get_current_user_info(request)
    if not username:
        return RedirectResponse(url="/login", status_code=303)
    logger.info(f"{username}获查看心电数据页")
    page = max(1, page)  # 确保页码至少为1
    page_size = 10

    # 管理员/超级管理员：查看所有设备（分页 + 搜索）
    if role in ["admin", "superadmin"]:
        query = db.query(Device)

        # 按用户名搜索
        if q:
            query = query.filter(Device.owner.contains(q))

        total_devices = query.count()
        total_pages = max(1, (total_devices + page_size - 1) // page_size)

        if page > total_pages:
            page = total_pages

        devices = query.offset((page - 1) * page_size).limit(page_size).all()

    # 医生：查看名下患者的设备（分页 + 搜索）
    elif role == "doctor":
        doctor = db.query(models.User).filter(models.User.username == username).first()
        patient_usernames = [
            p.username for p in db.query(models.User)
            .filter(models.User.role == "user", models.User.doctor_id == doctor.id)
        ]

        query = db.query(Device).filter(Device.owner.in_(patient_usernames))

        # 按用户名搜索
        if q:
            query = query.filter(Device.owner.contains(q))

        total_devices = query.count()
        total_pages = max(1, (total_devices + page_size - 1) // page_size)

        if page > total_pages:
            page = total_pages

        devices = query.offset((page - 1) * page_size).limit(page_size).all()

    # 普通用户：自己的设备（分页，不支持搜索）
    else:
        total_devices = db.query(Device).filter(Device.owner == username).count()
        total_pages = max(1, (total_devices + page_size - 1) // page_size)

        if page > total_pages:
            page = total_pages

        devices = db.query(Device).filter(Device.owner == username).offset((page - 1) * page_size).limit(
            page_size).all()

    # 核心：强制高亮心电菜单
    return templates.TemplateResponse(
        "dashboard.html",
        {
            "request": request,
            "username": username,
            "role": role,
            "devices": devices,
            "active_tab": "ecg",
            "current_page": page,
            "total_pages": total_pages,
            "search_q": q  # 返回搜索词
        }
    )
# 查看设备的数据记录列表
@router.get("/ecg/{device_id}", response_class=HTMLResponse)
def ecg_device_detail(
    request: Request,
    device_id: str,
    record_id: int = Query(None),
    db: Session = Depends(get_db)
):
    username, role = get_current_user_info(request)
    if not username:
        return RedirectResponse(url="/login", status_code=303)
    role = role.strip().lower()
    logger.info(f"{username}查看设备{device_id}的数据记录")
    # 查询设备
    device = db.query(Device).filter(Device.device_id == device_id).first()
    if not device:
        return RedirectResponse(url="/ecg?msg=device_not_exits", status_code=303)
    if role == "user":
        # 普通用户只能查看自己的设备
        if device.owner != username:
            logger.warning(f"用户 {username} 无权查看设备 {device_id}")
            return RedirectResponse(url="/ecg?msg=permission_denied", status_code=303)
    elif role == "doctor":
        # 医生只能查看自己患者的设备
        owner_user = db.query(User).filter(User.username == device.owner).first()
        if not owner_user or not owner_user.doctor or owner_user.doctor.username != username:
            logger.warning(f"医生 {username} 无权查看患者 {device.owner} 的设备 {device_id}")
            return RedirectResponse(url="/ecg?msg=permission_denied", status_code=303)
        logger.info(f"医生 {username} 查看患者 {owner_user.username} 的设备 {device_id}")

    elif role in ["admin", "superadmin"]:
        # 管理员/超级管理员无需限制
        logger.info(f"{role} {username} 查看设备 {device_id}")

    else:
        # 其他角色直接拒绝
        logger.warning(f"未知角色 {role} 尝试访问设备 {device_id}")
        return RedirectResponse(url="/ecg?msg=permission_denied", status_code=303)
    # 调试：查询前先检查device_id
    ecg_records = (
        db.query(ECGRecord)
        .filter(ECGRecord.device_id == device_id)
        .order_by(ECGRecord.start_time.desc())
        .all()
    )
    # 调试：查看查询结果
    if len(ecg_records) > 0:
        logger.info(f"第一条记录: ID={ecg_records[0].id}, 数据长度={len(ecg_records[0].data or '')}")
    else:
        # 如果没查到，看看数据库里到底有什么
        all_devices = db.query(ECGRecord.device_id).distinct().all()
        logger.warning(f"数据库中的所有设备ID: {[d[0] for d in all_devices]}")

    # 自动选中最新有数据的记录
    if record_id is None:
        for r in ecg_records:
            if r.data:
                record_id = r.id
                break
    ecg_records_json = [
        {
            "id": r.id,
            "device_id": r.device_id,
            "owner": r.owner,
            "start_time": r.start_time.strftime("%Y-%m-%d %H:%M:%S") if r.start_time else "",
            "end_time": r.end_time.strftime("%Y-%m-%d %H:%M:%S") if r.end_time else "",
            "data": r.data or "",
        }
        for r in ecg_records
    ]

    return templates.TemplateResponse(
        "ecg_device.html",
        {
            "request": request,
            "username": username,
            "device": device,
            "records": ecg_records_json,
            "record_id": record_id or 0,
            "active_tab": "ecg_records",
        },
    )

# ------------------- ECG 上传接口（性能优化版）-------------------
@router.post("/ecg_upload")
async def ecg_upload(
        device_id: str = Form(...),
        data: str = Form(...),
        timestamps: Optional[str] = Form(None),
        db: Session = Depends(get_db)
):
    # ========== 优化1：快速数据验证 ==========
    if not data:
        return {"msg": "empty"}

    # ========== 优化2：Redis缓存owner，避免每次查设备表 ==========
    cache_key_owner = f"owner:{device_id}"
    device_owner = redis_cache.get(cache_key_owner)

    if not device_owner:
        device = db.query(Device).filter(Device.device_id == device_id).first()
        if not device:
            return {"msg": "no_device"}
        device_owner = device.owner
        redis_cache.set(cache_key_owner, device_owner, 7200)  # 缓存2小时

    now = now_cst()
    last_record_id = redis_cache.get(f"last_record:{device_id}")

    # ========== 优化3：减少数据库往返（合并为单次UPDATE）==========
    if last_record_id:
        try:
            result = db.execute(text("""
                UPDATE ecg_records 
                SET data = CONCAT(COALESCE(data, ''), ',', :new_data),
                    timestamps = CONCAT(COALESCE(timestamps, ''), ',', :new_ts),
                    end_time = :end_time
                WHERE id = :rid
                  AND TIMESTAMPDIFF(MINUTE, start_time, :now) < 10
            """), {
                "new_data": data,
                "new_ts": timestamps or "",
                "end_time": now,
                "rid": last_record_id,
                "now": now
            })
            db.commit()

            if result.rowcount > 0:
                # ========== 修复：UPDATE分支也要100%检测 ==========
                import threading
                threading.Thread(
                    target=async_detection_task,
                    args=(data, timestamps, device_id, int(last_record_id), device_owner),
                    daemon=True
                ).start()

                return {"msg": "ok", "rid": last_record_id}

        except Exception as e:
            logger.error(f"Update error: {e}")
            db.rollback()

    # ========== 优化5：新建记录时移除refresh（减少SELECT）==========
    new_record = ECGRecord(
        device_id=device_id,
        owner=device_owner,
        start_time=now,
        data=data,
        timestamps=timestamps or ""
    )
    db.add(new_record)
    db.flush()  # 使用flush获取ID，不触发额外查询

    record_id = new_record.id
    db.commit()

    # 缓存记录ID
    redis_cache.set(f"last_record:{device_id}", str(record_id), 600)

    # ========== 修复：新建记录也要100%检测 ==========
    import threading
    threading.Thread(
        target=async_detection_task,
        args=(data, timestamps, device_id, record_id, device_owner),
        daemon=True
    ).start()

    logger.info(f"成功上传数据到设备{device_id}")
    return {"msg": "ok", "rid": record_id}


# ========== 异步检测任务（独立线程）==========
def async_detection_task(data, timestamps, device_id, record_id, owner):
    """后台线程执行检测"""
    try:
        is_abnormal = detect_abnormal_heart_rate(data, timestamps, device_id=device_id)

        if is_abnormal:
            db = SessionLocal()
            try:
                device = db.query(Device).filter(Device.device_id == device_id).first()
                if device:
                    create_alert(device, now_cst(), db, ecg_record_id=record_id)
            finally:
                db.close()
    except Exception as e:
        logger.error(f"异步检测失败: {e}")
# ========== 异步检测函数 ==========
def async_detect_and_alert(data, timestamps, device_id, record_id, owner, timestamp_str):
    """在独立线程中执行异常检测和报警"""
    try:
        from auth.db import SessionLocal

        # 异常检测
        is_abnormal = detect_abnormal_heart_rate(data, timestamps, device_id=device_id)

        if is_abnormal:
            # 创建新的数据库会话
            db = SessionLocal()
            try:
                device = db.query(Device).filter(Device.device_id == device_id).first()
                if device:
                    now = now_cst()
                    create_alert(device, now, db, ecg_record_id=record_id)
            finally:
                db.close()

    except Exception as e:
        logger.error(f"异步检测失败: {e}")
# ---------- 异常心律检测算法 --------------
# 使用有序字典 + LRU策略,限制最多缓存100个设备
device_state = OrderedDict()
MAX_DEVICE_CACHE = 100
def detect_abnormal_heart_rate(data, timestamps, sampling_rate=250, device_id=None):
    """
    优化版异常检测 - 提高心动过速识别准确率
    """
    global device_state

    # ---------- 数据解析 ----------
    try:
        data_points = np.array([float(x) for x in str(data).split(",") if x.strip() != ""])
    except Exception as e:
        logger.error(f"数据解析失败: {e}")
        return False

    n = len(data_points)
    if n < int(0.5 * sampling_rate):
        logger.warning(f"数据太短 (n={n})")
        return False

    # ---------- 时间轴 ----------
    try:
        if not timestamps or len(str(timestamps)) < 5:
            ts = np.arange(0, n / sampling_rate, 1 / sampling_rate)
        else:
            ts_arr = [float(x) for x in str(timestamps).split(",") if x.strip() != ""]
            ts = np.array(ts_arr) if len(ts_arr) == n else np.arange(0, n / sampling_rate, 1 / sampling_rate)
    except Exception:
        ts = np.arange(0, n / sampling_rate, 1 / sampling_rate)

    # ---------- 平滑去噪（减少平滑强度，保留R峰）----------
    window = max(3, int(0.015 * sampling_rate))  # 从0.02降到0.015
    kernel = np.ones(window) / window
    smooth = np.convolve(data_points, kernel, mode="same")

    # ---------- 波形特征 ----------
    signal_std = float(np.std(smooth))
    signal_mean = float(np.mean(smooth))
    smoothness = float(np.mean(np.abs(np.diff(smooth))))
    amplitude_range = float(np.ptp(smooth))
    diff2 = float(np.mean(np.abs(np.diff(np.diff(smooth)))))

    logger.info(
        f"[ECG调试] std={signal_std:.3f}, smoothness={smoothness:.3f}, range={amplitude_range:.3f}, diff2={diff2:.6f}")

    # 修改：正弦波和平线信号都应该触发报警，而不是过滤掉
    if signal_std > 0.8 and smoothness < 0.01 and diff2 < 0.0005:
        logger.warning("波形过于平滑 → 疑似正弦波 → 触发报警")
        # 不再返回False，而是继续检测，最终会因为无法检测到R峰而报警
        # 或者直接返回True触发报警
        del data_points, smooth
        return True  # 直接报警

    if amplitude_range < 0.15:
        logger.warning("振幅太小 → 平线信号 → 触发报警")
        del data_points, smooth
        return True  # 直接报警

    # ---------- 改进R峰检测（更敏感）----------
    # 使用自适应阈值
    threshold = signal_mean + max(0.3 * signal_std, 0.08)  # 从0.4降到0.3，从0.1降到0.08
    min_interval_s = 0.3  # 从0.35降到0.3，允许更快心率
    min_distance = max(1, int(min_interval_s * sampling_rate))

    candidate_peaks = [
        i for i in range(1, n - 1)
        if smooth[i] > smooth[i - 1] and smooth[i] > smooth[i + 1] and smooth[i] > threshold
    ]

    peaks = []
    last_peak = -1_000_000
    for idx in candidate_peaks:
        if idx - last_peak >= min_distance:
            peaks.append(idx)
            last_peak = idx

    if len(peaks) < 1:
        logger.warning("未检测到R峰")
        del data_points, smooth
        return False

    peaks = np.array(peaks) / sampling_rate

    # ---------- LRU 缓存管理 ----------
    if len(device_state) >= MAX_DEVICE_CACHE and device_id not in device_state:
        oldest_device = next(iter(device_state))
        del device_state[oldest_device]
        logger.info(f"LRU清理: 移除设备 {oldest_device}")

    if device_id not in device_state:
        device_state[device_id] = {"last_peak": None, "rr_history": []}

    device_state.move_to_end(device_id)
    state = device_state[device_id]

    # ---------- 跨批次R峰累积 ----------
    if state["last_peak"] is not None:
        peaks = np.concatenate(([state["last_peak"]], peaks))

    rr_intervals = np.diff(peaks)
    rr_intervals = rr_intervals[(rr_intervals > 0.25) & (rr_intervals < 2.5)]  # 从0.3降到0.25

    if len(rr_intervals) == 0:
        logger.warning("RR间期过少，继续等待下一批信号")
        state["last_peak"] = peaks[-1]
        del data_points, smooth, peaks
        return False

    # 更新设备状态
    state["last_peak"] = peaks[-1]
    state["rr_history"].extend(rr_intervals.tolist())
    if len(state["rr_history"]) > 10:
        state["rr_history"] = state["rr_history"][-10:]

    # ---------- 计算节律特征 ----------
    mean_rr = np.mean(state["rr_history"])
    rr_std_ratio = np.std(state["rr_history"]) / mean_rr if mean_rr > 0 else 0
    heart_rate = 60.0 / mean_rr

    logger.info(
        f"[ECG调试] HR={heart_rate:.1f} bpm, RR变异度={rr_std_ratio:.3f}, 峰数={len(peaks)}, 缓存设备数={len(device_state)}")

    # ---------- 改进异常判定逻辑 ----------
    result = False

    # 核心优化：使用明确的心率范围判定
    if heart_rate < 40:
        logger.warning(f"心动过缓 (HR={heart_rate:.1f} bpm)")
        result = True
    elif heart_rate > 100:  # 保持100 bpm阈值
        logger.warning(f"心动过速 (HR={heart_rate:.1f} bpm)")
        result = True
    # 放宽RR变异度阈值（从0.01改为0.15）
    elif rr_std_ratio > 0.15:
        logger.warning(f"RR变异度过高 ({rr_std_ratio:.3f}) → 心律不齐")
        result = True
    elif np.any(np.array(state["rr_history"]) < 0.5 * mean_rr):  # 从0.6降到0.5
        logger.warning("检测到提前搏动（PAC）")
        result = True
    else:
        logger.info("检测结果：正常")

    # 清理临时变量
    del data_points, smooth, peaks

    return result
# ========== 管理接口 ==========

def get_device_cache_stats():
    """获取设备缓存统计信息"""
    return {
        "cached_devices": len(device_state),
        "max_cache": MAX_DEVICE_CACHE,
        "device_ids": list(device_state.keys())
    }


def clear_device_cache():
    """清空所有设备缓存"""
    global device_state
    count = len(device_state)
    device_state.clear()
    import gc
    gc.collect()
    return {"cleared_count": count}
# ------------------- 报警生成 -------------------
def create_alert(device,now, db, ecg_record_id=None):
    """生成报警记录并关联心电数据"""
    if isinstance(now, str):
        try:
            now = now_cst()
        except Exception:
            now = now_cst()

    alert_message = f"设备 {device.device_id} 于 {now.strftime('%Y-%m-%d %H:%M:%S')} 检测到异常心律"

    alert = Alert(
        device_id=device.device_id,
        owner=device.owner,
        message=alert_message,
        timestamp=now,
        ecg_record_id=ecg_record_id  # 新增关联字段
    )

    db.add(alert)
    db.commit()
    logger.info(f"{device.device_id}报警生成成功")
    print(f"报警生成成功: {device.device_id} (关联记录ID={ecg_record_id})")